Skip to content

feat(Nav): add icon prop to NavExpandable#12448

Closed
rebeccaalpert wants to merge 1 commit into
patternfly:mainfrom
rebeccaalpert:cursor/nav-expandable-icon-prop
Closed

feat(Nav): add icon prop to NavExpandable#12448
rebeccaalpert wants to merge 1 commit into
patternfly:mainfrom
rebeccaalpert:cursor/nav-expandable-icon-prop

Conversation

@rebeccaalpert

@rebeccaalpert rebeccaalpert commented Jun 11, 2026

Copy link
Copy Markdown
Member

Align expandable nav sections with NavItem by supporting an optional leading icon.

Assisted-by: Cursor

Screenshot 2026-06-11 at 2 44 29 PM

What: Closes #12443

Additional issues:

Summary by CodeRabbit

  • New Features

    • Expandable navigation items now support optional icons displayed before the title.
  • Tests

    • Added test coverage verifying icon rendering and styling behavior.
  • Documentation

    • Added example demonstrating usage of icons with expandable navigation items.

Align expandable nav sections with NavItem by supporting an optional leading icon.

Assisted-by: Cursor
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

NavExpandable component now accepts an optional icon prop rendered before expandable children. The feature includes prop type definition, conditional JSX rendering, unit tests validating icon display behavior, and a new example demonstrating usage with multiple icon-bearing expandable groups and state management.

Changes

NavExpandable Icon Support

Layer / File(s) Summary
Icon prop contract and implementation
packages/react-core/src/components/Nav/NavExpandable.tsx, packages/react-core/src/components/Nav/__tests__/NavExpandable.test.tsx
NavExpandableProps adds icon?: React.ReactNode; render() destructures and conditionally renders the icon inside a <span> with navLinkIcon class. Tests verify icon presence when provided and element absence when omitted.
Documentation and usage example
packages/react-core/src/components/Nav/examples/Nav.md, packages/react-core/src/components/Nav/examples/NavExpandableIcons.tsx
Nav.md documents a new "Expandable with icons" example. NavExpandableIcons component renders two icon-bearing expandable groups with NavItems, manages active group/item state, and logs toggle events.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

Needs design review

Suggested reviewers

  • nicolethoen
  • bekah-stephens
  • wise-king-sullyman
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: adding an icon prop to the NavExpandable component.
Linked Issues check ✅ Passed The PR fully implements the requested feature: adds the optional icon prop to NavExpandable, includes comprehensive tests, and provides example documentation.
Out of Scope Changes check ✅ Passed All changes are directly related to the feature request: component prop addition, test coverage for the icon prop, and documentation examples.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@patternfly-build

patternfly-build commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/react-core/src/components/Nav/examples/NavExpandableIcons.tsx (1)

26-88: 💤 Low value

Consider demonstrating toggle behavior more clearly.

Both expandable groups have isExpanded set to true. For a better demonstration of the expandable functionality with icons, consider having one group collapsed by default or controlling the expanded state to show the icon in both collapsed and expanded states.

📚 Alternative: control expanded state
 export const NavExpandableIcons: React.FunctionComponent = () => {
   const [activeGroup, setActiveGroup] = useState('nav-expandable-icon-group-1');
   const [activeItem, setActiveItem] = useState('nav-expandable-icon-group-1_item-1');
+  const [expandedGroups, setExpandedGroups] = useState<string[]>(['nav-expandable-icon-group-1']);

   const onSelect = (
     _event: React.FormEvent<HTMLInputElement>,
     result: { itemId: number | string; groupId: number | string }
   ) => {
     setActiveGroup(result.groupId as string);
     setActiveItem(result.itemId as string);
   };

   const onToggle = (
     _event: React.MouseEvent<HTMLButtonElement>,
     result: { groupId: number | string; isExpanded: boolean }
   ) => {
     // eslint-disable-next-line no-console
     console.log(`Group ${result.groupId} expanded? ${result.isExpanded}`);
+    setExpandedGroups(prev => 
+      result.isExpanded 
+        ? [...prev, result.groupId as string]
+        : prev.filter(id => id !== result.groupId)
+    );
   };

   return (
     <Nav onSelect={onSelect} onToggle={onToggle} aria-label="Expandable with icons global">
       <NavList>
         <NavExpandable
           title="Expandable Group 1"
           icon={<CubeIcon />}
           groupId="nav-expandable-icon-group-1"
           isActive={activeGroup === 'nav-expandable-icon-group-1'}
-          isExpanded
+          isExpanded={expandedGroups.includes('nav-expandable-icon-group-1')}
         >
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-core/src/components/Nav/examples/NavExpandableIcons.tsx`
around lines 26 - 88, The example always forces both NavExpandable groups open
via the isExpanded prop; change it to demonstrate toggle behavior by controlling
expansion from state instead. Replace the hardcoded isExpanded on each
NavExpandable with a derived value (e.g., isExpanded={activeGroup ===
'nav-expandable-icon-group-1'} and isExpanded={activeGroup ===
'nav-expandable-icon-group-2'}) or manage a local expandedGroups state and
update it in the existing onToggle handler so one group can be collapsed by
default and toggles correctly; update NavExpandable usages accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/react-core/src/components/Nav/NavExpandable.tsx`:
- Line 138: The icon span rendered in NavExpandable (the JSX expression {icon &&
<span className={css(styles.navLinkIcon)}>{icon}</span>}) should be marked
decorative by adding aria-hidden="true" to that span; apply the identical change
to the equivalent icon wrapper in NavItem.tsx so both NavExpandable and NavItem
treat the displayed icons as non-accessible/decorative elements.

---

Nitpick comments:
In `@packages/react-core/src/components/Nav/examples/NavExpandableIcons.tsx`:
- Around line 26-88: The example always forces both NavExpandable groups open
via the isExpanded prop; change it to demonstrate toggle behavior by controlling
expansion from state instead. Replace the hardcoded isExpanded on each
NavExpandable with a derived value (e.g., isExpanded={activeGroup ===
'nav-expandable-icon-group-1'} and isExpanded={activeGroup ===
'nav-expandable-icon-group-2'}) or manage a local expandedGroups state and
update it in the existing onToggle handler so one group can be collapsed by
default and toggles correctly; update NavExpandable usages accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a78d09fe-46e4-4718-91f7-d232a555c19f

📥 Commits

Reviewing files that changed from the base of the PR and between 8b528bb and cbb5f9b.

📒 Files selected for processing (4)
  • packages/react-core/src/components/Nav/NavExpandable.tsx
  • packages/react-core/src/components/Nav/__tests__/NavExpandable.test.tsx
  • packages/react-core/src/components/Nav/examples/Nav.md
  • packages/react-core/src/components/Nav/examples/NavExpandableIcons.tsx

tabIndex={isSidebarOpen ? null : -1}
{...buttonProps}
>
{icon && <span className={css(styles.navLinkIcon)}>{icon}</span>}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check NavItem icon wrapper implementation for aria-hidden

rg -nP -A2 -B2 'navLinkIcon.*icon' --type=tsx --type=ts packages/react-core/src/components/Nav/NavItem.tsx

Repository: patternfly/patternfly-react

Length of output: 99


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate NavItem.tsx and inspect icon wrapper + aria-hidden usage
ls -la packages/react-core/src/components/Nav/ || true

if [ -f packages/react-core/src/components/Nav/NavItem.tsx ]; then
  rg -n 'navLinkIcon' packages/react-core/src/components/Nav/NavItem.tsx
  rg -n 'aria-hidden' packages/react-core/src/components/Nav/NavItem.tsx || true
  # show small surrounding area where icon wrapper is rendered
  rg -n --context=4 'navLinkIcon' packages/react-core/src/components/Nav/NavItem.tsx
else
  echo "NavItem.tsx not found at expected path" >&2
fi

# Also inspect NavExpandable.tsx around the referenced line for consistency
rg -n --context=3 'navLinkIcon' packages/react-core/src/components/Nav/NavExpandable.tsx

Repository: patternfly/patternfly-react

Length of output: 1596


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/react-core/src/components/Nav/NavItem.tsx"

echo "== aria-hidden matches in NavItem.tsx =="
rg -n 'aria-hidden' "$FILE" || true

echo "== navLinkIcon context in NavItem.tsx =="
rg -n --context=4 'navLinkIcon' "$FILE"

Repository: patternfly/patternfly-react

Length of output: 504


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/react-core/src/components/Nav/NavExpandable.tsx"

echo "== aria-hidden matches in NavExpandable.tsx =="
rg -n 'aria-hidden' "$FILE" || true

echo "== navLinkIcon context in NavExpandable.tsx =="
rg -n --context=4 'navLinkIcon' "$FILE"

Repository: patternfly/patternfly-react

Length of output: 679


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '180,240p' packages/react-core/src/components/Nav/NavItem.tsx | cat -n

echo "----"

sed -n '110,170p' packages/react-core/src/components/Nav/NavExpandable.tsx | cat -n

Repository: patternfly/patternfly-react

Length of output: 5416


Add aria-hidden="true" to decorative nav icon wrappers in NavExpandable/NavItem

Both packages/react-core/src/components/Nav/NavExpandable.tsx and packages/react-core/src/components/Nav/NavItem.tsx render the icon span without aria-hidden, even though the icon sits next to visible text.

♿ Proposed fix to add aria-hidden
-                        {icon && <span className={css(styles.navLinkIcon)}>{icon}</span>}
+                        {icon && <span className={css(styles.navLinkIcon)} aria-hidden="true">{icon}</span>}

Apply the same aria-hidden="true" to the equivalent icon wrapper in NavItem.tsx.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{icon && <span className={css(styles.navLinkIcon)}>{icon}</span>}
{icon && <span className={css(styles.navLinkIcon)} aria-hidden="true">{icon}</span>}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-core/src/components/Nav/NavExpandable.tsx` at line 138, The
icon span rendered in NavExpandable (the JSX expression {icon && <span
className={css(styles.navLinkIcon)}>{icon}</span>}) should be marked decorative
by adding aria-hidden="true" to that span; apply the identical change to the
equivalent icon wrapper in NavItem.tsx so both NavExpandable and NavItem treat
the displayed icons as non-accessible/decorative elements.

@rebeccaalpert

rebeccaalpert commented Jun 12, 2026

Copy link
Copy Markdown
Member Author

I was assigned this in Jira. After putting this together, I noticed an existing PR by @logonoff. Will bring to group and see what we want to do with this duplicated work. We may be able to just close this one.

@logonoff logonoff left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicate of #12444, should i close mine?

@logonoff

logonoff commented Jun 12, 2026

Copy link
Copy Markdown
Member

heh

I was assigned this in Jira. After putting this together, I noticed an existing PR by @logonoff. Will bring to group and see what we want to do with this duplicated work. We may be able to just close this one.

image

@rebeccaalpert

Copy link
Copy Markdown
Member Author

I'm going to close this one and I'll work on reviewing yours/getting it in @logonoff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NavExpandable - Supprt icon prop like NavItem

3 participants